/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package net.jueb.socks5;

import java.util.HashMap;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.socks.SocksAuthRequest;
import io.netty.handler.codec.socks.SocksAuthRequestDecoder;
import io.netty.handler.codec.socks.SocksAuthResponse;
import io.netty.handler.codec.socks.SocksAuthScheme;
import io.netty.handler.codec.socks.SocksAuthStatus;
import io.netty.handler.codec.socks.SocksCmdRequest;
import io.netty.handler.codec.socks.SocksCmdRequestDecoder;
import io.netty.handler.codec.socks.SocksCmdType;
import io.netty.handler.codec.socks.SocksInitResponse;
import io.netty.handler.codec.socks.SocksRequest;
import io.netty.util.AttributeKey;

/**
 * 业务处理handler
 * @author helin
 *
 */
@ChannelHandler.Sharable
public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksRequest> {
    private static final String name = "SOCKS_SERVER_HANDLER";
    private static HashMap<String,String> config;
    public static String getName() {
        return name;
    }
    public SocksServerHandler(HashMap<String,String> config) {
		this.config=config;
	}
    @Override
    public void messageReceived(ChannelHandlerContext ctx, SocksRequest socksRequest) throws Exception {
    	switch (socksRequest.requestType()) {
            case INIT: {
//            	auth support example
        		//设置认证
        		boolean auth=true;
        		if(config.containsKey("auth"))
        		{
        			try {
        				auth=Boolean.valueOf(config.get("auth"));
        			} catch (Exception e) {
        				e.printStackTrace();
        			}
        		}
        		if(auth)
        		{
        			//设置下次要执行的处理器-获取认证
                    ctx.pipeline().addFirst("socksAuthRequestDecoder",new SocksAuthRequestDecoder());//协商认证协议
                    ctx.write(new SocksInitResponse(SocksAuthScheme.AUTH_PASSWORD));//回复认证协议类型
        		}else
        		{//无认证
        		    //读取协商请求
                	ctx.pipeline().addFirst(SocksCmdRequestDecoder.getName(), new SocksCmdRequestDecoder());
                    //回复协商协议 如果是无认证，则客户端下一步就直接发送转发请求了
                	ctx.write(new SocksInitResponse(SocksAuthScheme.NO_AUTH));
        		}
                break;
            }
            case AUTH:
            	//读取认证
            	SocksAuthRequest user=(SocksAuthRequest)socksRequest;
            	System.out.println(user.username()+":"+user.password());//显示用户名密码
            	if(!user.username().equals("port") && !user.username().equals("auth") )
            	{//如果用户名不是配置名称
            		if(config.containsKey(user.username()) && user.password().equals(config.get(user.username())))
            		{//如果存在用户，且密码正确
            			ctx.pipeline().addFirst(SocksCmdRequestDecoder.getName(), new SocksCmdRequestDecoder());
                		ctx.write(new SocksAuthResponse(SocksAuthStatus.SUCCESS));
                		break;//认证成功并跳出
            		}
            	}
            	//认证失败，则关闭连接并且跳出
            	ctx.write(new SocksAuthResponse(SocksAuthStatus.FAILURE));
        		ctx.close();
                break;
            case CMD://转发请求命令
                SocksCmdRequest req = (SocksCmdRequest) socksRequest;
                if (req.cmdType() == SocksCmdType.CONNECT) 
                {//如果是连接类型的代理请求，则添加相应handler处理
                    ctx.pipeline().addLast(SocksServerConnectHandler.getName(), new SocksServerConnectHandler());
                    ctx.pipeline().remove(this);//并移除当前handler
                    ctx.fireChannelRead(socksRequest);//把当前的request设置为下一个handler的处理请求
                } else 
                {
                    ctx.close();
                }
                break;
            case UNKNOWN:
                ctx.close();
                break;
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise)
    		throws Exception {
    	super.close(ctx, promise);
    	System.out.println(ctx.channel().id());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) throws Exception {
        throwable.printStackTrace();
        SocksServerUtils.closeOnFlush(ctx.channel());
    }
}
